home *** CD-ROM | disk | FTP | other *** search
/ MPEG Toolkit / MPEG Toolkit.iso / dos / infompeg / infompeg.c next >
C/C++ Source or Header  |  1997-01-01  |  13KB  |  428 lines

  1. /*
  2.  * InfoMPEG version 1.0
  3.  * Copyright (C) 1993 Dennis Lee
  4.  */
  5.  
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <ctype.h>
  10. #include "infompeg.h"
  11.  
  12. #ifdef BORLAND
  13.  #include <dir.h>
  14.  #include <dos.h>
  15. #endif
  16.  
  17. /*
  18.  * Usage of the program.
  19.  */
  20. void
  21. usage() {
  22.     fprintf(stderr,"\nUsage: InfoMPEG [-123] filename.mpg [filename.mpg ...]\n\n");
  23.     fprintf(stderr,"       1 - Reports resolution and frame types(IPB) present(very fast)\n");
  24.     fprintf(stderr,"       2 - More detail than 1, including # of each frame type\n");
  25.     fprintf(stderr,"           present and overall compression\n");
  26.     fprintf(stderr,"       3 - (Default) Reports the most information\n\n");
  27.     fprintf(stderr,"InfoMPEG by Dennis Lee\n");
  28.     exit(0);
  29.     }
  30.  
  31. #ifdef BORLAND
  32. /*
  33.  * Strips the name of the file specified leaving only the path to it.
  34.  */
  35. char *
  36. get_path (char *name) {
  37.     int i;
  38.     for(i = strlen(name)-1; name[i] != '\\' && name[i] != '/' && name[i] != ':'; i--)
  39.     name[i] = 0;
  40.     return name;
  41.     }
  42.  
  43. /*
  44.  * Expands '*.MPG' expressions specified on the command line.
  45.  */
  46. void
  47. expand_cmd_line (short *argc, char **argv[]) {
  48.     yes_or_no files_left, do_expand;
  49.     struct ffblk tmp_file_info;
  50.     char file_mask[80], tmp_path[80];
  51.     short i, j, new_argc=0;
  52.     char **argv_buffer = (char **)malloc(MAX_FILES * sizeof(char *));
  53.  
  54.     for(i=0; i < *argc; i++) {
  55.     do_expand = NO;    /* Assume there is no wildcard '*' until one is found */
  56.     for(j = strlen((*argv)[i])-1; j >= 0; j--) {
  57.         if ((*argv)[i][j] == '*') {
  58.         do_expand = YES;
  59.         break;
  60.         }
  61.         }
  62.     if (do_expand) {
  63.         strcpy(file_mask, (*argv)[i]);
  64.         strcpy(tmp_path, get_path((*argv)[i]));
  65.         files_left = !findfirst(file_mask, &tmp_file_info, FA_RDONLY);
  66.         while (files_left) {
  67.         argv_buffer[new_argc] = (char *)malloc(80 * sizeof(char));
  68.         strcpy(argv_buffer[new_argc], tmp_path);
  69.         strcat(argv_buffer[new_argc++], tmp_file_info.ff_name);
  70.         files_left = !findnext(&tmp_file_info);
  71.         }
  72.         }
  73.     else {
  74.         argv_buffer[new_argc] = (char *)malloc(80 * sizeof(char));
  75.         strcpy(argv_buffer[new_argc++], (*argv)[i]);
  76.         }
  77.     }
  78.     *argc = new_argc;
  79.     *argv = argv_buffer;
  80.     }
  81. #endif
  82.  
  83. /*
  84.  * Strips the full path of the file specified leaving only the filename.
  85.  */
  86. void
  87. get_name (char *name) {
  88.     int i, j, pos=0;
  89.     for (i=strlen(name)-1;i>=0;i--)
  90.     if (name[i]=='\\' || name[i]=='/' || name[i]==':')
  91.         break;
  92.     if (i==0) i--;
  93.     for (j=i+1; j <= strlen(name); j++)
  94.     name[pos++]=toupper(name[j]);
  95.     }
  96.  
  97. /*
  98.  * Reads the next 24 bits in the stream for the pixel resolution
  99.  * of the MPEG file. 12 bits each for height and width, respectively.
  100.  */
  101. void
  102. get_stream_res(FILE *stream) {
  103.     long byte1, byte2, byte3;
  104.     byte1 = getc(stream);
  105.     byte2 = getc(stream);
  106.     byte3 = getc(stream);
  107.     width = (byte1 << 4) | (byte2 >> 4);
  108.     height = ((byte2 & 0x0f) << 8) | byte3;
  109.     }
  110.  
  111. /*
  112.  * This procedure is called after a picture start code has just been
  113.  * read, and it proceeds to read info on the current frame, such as frame
  114.  * type(IPB), and length.  The procedure also has code to find a recursive
  115.  * frame sequence if one exists.
  116.  */
  117. void
  118. get_frame_info (yes_or_no get_all_data, FILE *stream) {
  119.     frame_info cur_frame;
  120.     short cur_byte;
  121.  
  122.     cur_frame.frame_len = ftell(stream) - file_offset;
  123.     file_offset = ftell(stream);
  124.  
  125.     if (previous_frame != 0) {      /* if a previous frame exists */
  126.     switch (previous_frame) {
  127.         case I: frame_sums[0].sum_frame_len += cur_frame.frame_len;  break;
  128.         case P: frame_sums[1].sum_frame_len += cur_frame.frame_len;  break;
  129.         case B: frame_sums[2].sum_frame_len += cur_frame.frame_len;
  130.         }
  131.     }
  132.  
  133.     if (get_all_data) {
  134.     cur_byte=getc(stream);
  135.     cur_byte=getc(stream);
  136.     cur_frame.frame_type = (cur_byte >> 3) & 7;
  137.     previous_frame = cur_frame.frame_type;
  138.     switch (cur_frame.frame_type) {
  139.         case I: frame_sums[0].sum_frames++;  break;
  140.         case P: frame_sums[1].sum_frames++;  break;
  141.         case B: frame_sums[2].sum_frames++;
  142.         }
  143.     if (cur_frame.frame_type==I && frame_sums[0].sum_frames==3) {
  144.         frame_cycle[cycle_len]=0;
  145.         cycle_len=0;
  146.         }
  147.     if (frame_sums[0].sum_frames==2)
  148.         frame_cycle[cycle_len++] = cur_frame.frame_type;
  149.     else if (frame_sums[0].sum_frames==3) {
  150.         if (frame_cycle[cycle_len++] != cur_frame.frame_type)
  151.         cycle_exists=FALSE;
  152.         }
  153.     total_frames++;
  154.     if (report_info_type != 1) {
  155.         printf("%c", frame_types[cur_frame.frame_type]);
  156.         if ((total_frames % 64) == 0)
  157.         printf("\n");
  158.         fflush(stdout);
  159.         }
  160.     }
  161.     }
  162.  
  163. /*
  164.  * Parses the stream sequentially until a start code is found.
  165.  * (a start code begins with 0x000001)
  166.  * The last byte of the start code is left in the stream for an external
  167.  * procedure to use (it specifies the type of start code).
  168.  */
  169. void
  170. next_start_code (FILE *stream) {
  171.     long counter=0;
  172.     while(1) {
  173.     switch(getc(stream)) {
  174.         case EOF:  return;
  175.         case 0x00: counter++; break;
  176.         case 0x01: if (counter >= 2) return;
  177.         default:   counter=0;
  178.         }
  179.     }
  180.     }
  181.  
  182. /*
  183.  * Initializes all global variables for every new MPEG stream to be parsed.
  184.  */
  185. void
  186. init_vars() {
  187.     int i;
  188.     file_offset=0;
  189.     total_frames=0;
  190.     previous_frame=0;
  191.     cycle_exists=TRUE;
  192.     cycle_len=0;
  193.     for(i=0; i < 3; i++) {
  194.     frame_sums[i].sum_frames=0;
  195.     frame_sums[i].sum_frame_len=0;
  196.     }
  197.     }
  198.  
  199. /*
  200.  * Collects info on the MPEG stream specified and presents them.
  201.  */
  202. void
  203. report_info (char *filename, FILE *stream) {
  204.     boolean error_exit=FALSE, finish=FALSE;
  205.     static first_call=TRUE;
  206.     char tmp_string[15];
  207.  
  208.     init_vars();
  209.     get_name(filename);
  210.     get_stream_res(stream);
  211.     if (report_info_type==1) {
  212.     if (first_call) {
  213.         first_call=FALSE;
  214.         printf("\nInfoMPEG version 1.0\n");
  215.         printf("--------------------\n");
  216.         printf("Filename                  Frame Resolution     Frame Types Present\n");
  217.         printf("--------                  ----------------     -------------------\n");
  218.         }
  219.     while(!finish) {
  220.         next_start_code(stream);
  221.       /*
  222.        *  The last byte of a start code is read.  If the byte is
  223.        *  0x00 - data to reconstruct a frame follows
  224.        *  0xB7 - the MPEG stream ends (0x000001B7 is the sequence end code)
  225.        *  EOF  - a premature EOF has been encountered indicating the
  226.        *         bitstream is missing a sequence end code
  227.        */
  228.         switch (getc(stream)) {
  229.         case 0x00: get_frame_info(YES, stream);     break;
  230.         case 0xB7: error_exit=FALSE;  finish=TRUE;  break;
  231.         case EOF:  error_exit=TRUE;   finish=TRUE;
  232.         }
  233.         /* 
  234.          *  Finishes when at least 1 of each frame type is found or 3
  235.          *  I frames have been encountered.  It is assumed that some sort
  236.          *  of recursive frame sequence exists, in which case all frame
  237.          *  types in the stream have already been found.
  238.          */
  239.         if (frame_sums[0].sum_frames==3 || (frame_sums[1].sum_frames != 0
  240.                         && frame_sums[2].sum_frames != 0))
  241.         finish=TRUE;
  242.         }
  243.     sprintf(tmp_string,"(%ldx%ld)", width, height);
  244.     printf("%-29s%-26s",filename,tmp_string);
  245.     /* Assumes an I frame is always present */
  246.     printf("I");
  247.     if (frame_sums[1].sum_frames != 0)
  248.         printf("P");
  249.     if (frame_sums[2].sum_frames != 0)
  250.         printf("B");
  251.     printf("\n");
  252.     }
  253.     else {
  254.     short tmp_compression;
  255.     long filesize, uncomp_len, i;
  256.     printf("\nInfoMPEG version 1.0\n");
  257.     printf("--------------------\n");
  258.     printf("%s is (%ldx%ld)\n\n", filename, width, height);
  259.     printf("Sequence of Frames (Decoder's Viewpoint)\n");
  260.     printf("----------------------------------------\n");
  261.     while(!finish) {
  262.         next_start_code(stream);
  263.         switch (getc(stream)) {
  264.         case 0x00: get_frame_info(YES, stream);     break;
  265.         case 0xB7: error_exit=FALSE;  finish=TRUE;  break;
  266.         case EOF:  error_exit=TRUE;   finish=TRUE;
  267.         }
  268.         }
  269.     get_frame_info(NO, stream);
  270.  
  271.     printf("\n\nRecursive frame type sequence : ");
  272.     if (cycle_exists && (frame_sums[0].sum_frames > 3)) {
  273.         short i;
  274.         for(i=0; i < cycle_len; i++)
  275.         printf("%c", frame_types[frame_cycle[i]]);
  276.         printf("\n                       Length : %d\n\n",cycle_len);
  277.         }
  278.     else
  279.         printf("None found.\n\n");
  280.  
  281.     filesize = ftell(stream);
  282.     uncomp_len = width*height*3*total_frames;
  283.     if (report_info_type==3) {
  284.         for(i=0; i < 3; i++) {
  285.         if (frame_sums[i].sum_frames != 0) {
  286.         printf("# of %c Frames : %d\n", frame_types[i+1], frame_sums[i].sum_frames);
  287.         printf("Average Size  : %ld Bytes\n", frame_sums[i].sum_frame_len/
  288.                               frame_sums[i].sum_frames);
  289.         printf("Compression   : %ld:%ld  or\n", width*height*3*frame_sums[i].sum_frames,
  290.                             frame_sums[i].sum_frame_len);
  291.         printf("                %d to 1\n\n", width*height*3*frame_sums[i].sum_frames/
  292.                               frame_sums[i].sum_frame_len);
  293.         }
  294.         }
  295.         printf("Total Frames        : %d\n", total_frames);
  296.         printf("Overall Avg Size    : %ld Bytes\n", filesize/total_frames);
  297.         printf("Overall Compression : %ld:%ld  or\n", uncomp_len, filesize);
  298.         tmp_compression = uncomp_len/filesize;
  299.         printf("                      %d to 1\n\n", tmp_compression);
  300.         }
  301.     else {
  302.         for(i=0; i < 3; i++) {
  303.         if (frame_sums[i].sum_frames != 0)
  304.             printf("# of %c Frames : %d\n", frame_types[i+1], frame_sums[i].sum_frames);
  305.         }
  306.         printf("Total Frames  : %d\n", total_frames);
  307.         printf("Overall Compression : %ld:%ld  or\n", uncomp_len, filesize);
  308.         tmp_compression = uncomp_len/filesize;
  309.         printf("                      %d to 1\n\n", tmp_compression);
  310.         }
  311.     if (num_files < MAX_FILES) {
  312.         strcpy(files[num_files].name, filename);
  313.         files[num_files].size = filesize;
  314.         tot_len += uncomp_len;
  315.         sprintf(tmp_string,"(%ldx%ld)", width, height);
  316.         strcpy(files[num_files].resolution, tmp_string);
  317.         files[num_files].frames = total_frames;
  318.         strcpy(tmp_string,"I");
  319.         if (frame_sums[1].sum_frames != 0)
  320.         strcat(tmp_string,"P");
  321.         if (frame_sums[2].sum_frames != 0)
  322.         strcat(tmp_string,"B");
  323.         strcpy(files[num_files].frame_types_found, tmp_string);
  324.         files[num_files++].compression = tmp_compression;
  325.         }
  326.     }
  327.     if (error_exit)
  328.     fprintf(stderr,"Error: Premature EOF in %s.\n",filename);
  329.     }
  330.  
  331. /*
  332.  * Sorts the MPEG streams in order of compression(greatest to least).
  333.  */
  334. void
  335. sort_files(short start, short end) {
  336.     file_info tmp;
  337.     int i, j, largest_left;
  338.     for(i = start; i < end; i++) {
  339.     tmp = files[i];
  340.     largest_left=i;
  341.     for(j = i+1; j <= end; j++) {
  342.         if (files[j].compression > tmp.compression) {
  343.         tmp = files[j];
  344.         largest_left = j;
  345.         }
  346.         }
  347.     tmp = files[i];
  348.     files[i] = files[largest_left];
  349.     files[largest_left] = tmp;
  350.     }
  351.     }
  352.  
  353. /*
  354.  * If more than one MPEG stream is specified on the command line, with
  355.  * 'report_info_type' not equal to 1, a chart listing the streams in order
  356.  * of compression(greatest to least) is displayed allowing comparisons
  357.  * between the streams.
  358.  */
  359. void
  360. report_comparison() {
  361.     ULONG i, tot_file_len=0;
  362.     printf("\n*********************************************************\n\n");
  363.     printf("MPEG Files (Ordered by Compression)\n");
  364.     printf("-----------------------------------\n");
  365.     printf("Filename        Filesize   Resolution  #Frames  Types  Compression\n");
  366.     printf("--------        --------   ----------  -------  -----  -----------\n");
  367.     sort_files(0, num_files-1);
  368.     for(i=0; i < num_files; i++) {
  369.     printf("%-17s%-10ld%-14s%-8d%-10s%d\n", files[i].name,
  370.                         files[i].size,
  371.                         files[i].resolution,
  372.                         files[i].frames,
  373.                         files[i].frame_types_found,
  374.                         files[i].compression);
  375.     tot_file_len += files[i].size;
  376.     }
  377.     printf("------------------------------------------------------------------\n");
  378.     printf("Total Filesizes: %-10ldTotal Compression: %ld:%ld  or\n", tot_file_len,
  379.                                       tot_len,
  380.                                       tot_file_len);
  381.     printf("                                              %d to 1\n", tot_len/
  382.                                       tot_file_len);
  383.     }
  384.  
  385. main(short argc, char *argv[]) {
  386.     FILE *mpeg_stream;
  387.     char filename[100];
  388.     short file_start=1;
  389.  
  390.     #ifdef BORLAND
  391.      expand_cmd_line(&argc, &argv);
  392.     #endif
  393.  
  394.     if (argc < 2)
  395.     usage();
  396.     else {
  397.     report_info_type = 3;
  398.     if (argv[1][0] == '-') {
  399.         file_start++;
  400.         switch (argv[1][1]) {
  401.         case '1': report_info_type = 1; break;
  402.         case '2': report_info_type = 2; break;
  403.         case '3': report_info_type = 3; break;
  404.         default : usage();
  405.         }
  406.         }
  407.     num_files=0;
  408.     for(;file_start < argc; file_start++) {
  409.         strcpy(filename,argv[file_start]);
  410.         if ((mpeg_stream = fopen(filename,"rb")) == NULL) {
  411.         fprintf(stderr,"Error: Cannot open file %s.\n",filename);
  412.         continue;
  413.         }
  414.         next_start_code(mpeg_stream);
  415.         if (getc(mpeg_stream) != 0xB3) {  /* 0xB3 is the last byte of the sequence start code */
  416.         fprintf(stderr,"Error: %s is not a valid MPEG video stream.\n",filename);
  417.         fclose(mpeg_stream);
  418.         continue;
  419.         }
  420.         report_info(filename, mpeg_stream);
  421.         fclose(mpeg_stream);
  422.         }
  423.     if (num_files > 1)
  424.         report_comparison();
  425.     }
  426.     return(0);
  427.     }
  428.